﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;

namespace Hsenl.Network {
    // 外包socket, 对socket进行封装
    public abstract class EpibolySocket : IEpibolySocket {
        private volatile bool _isClosed;
        private volatile bool _isDisposed;

        internal Socket Socket { get; set; }

        Socket IEpibolySocket.Socket {
            get => this.Socket;
            set => this.Socket = value;
        }

        public bool IsClosed => this._isClosed;
        public bool IsDisposed => this._isDisposed;

        public event Action<int> OnErrorEvent;
        public event Action OnClosedEvent;

        protected EpibolySocket(Socket socket) {
            this.Init(socket);
        }

        public void Init(Socket socket) {
            this.Socket = socket;
            this._isClosed = false;
            this.OnErrorEvent = null;
            this.OnClosedEvent = null;
        }

        // 以下几种接收方案都能接收到数据, 不局限于发送方使用的是那种发送方案
        protected virtual bool RecvAsync() {
            throw new NotImplementedException();
        }

        // 接收的同时, 能够拿到对方的地址
        protected virtual bool RecvFromAsync() {
            throw new NotImplementedException();
        }

        // 不仅能拿到地址, 还能拿到IP头部信息和SocketFlags
        protected virtual bool RecvMessageFromAsync() {
            throw new NotImplementedException();
        }

        // tcp
        protected virtual bool SendAsync(SocketFlags socketFlags = SocketFlags.None) {
            throw new NotImplementedException();
        }

        // tcp
        // 适合需要高效发送多个缓冲区或文件数据的场景
        protected virtual bool SendPacketsAsync(IList<ArraySegment<byte>> buffers, SocketFlags socketFlags = SocketFlags.None) {
            throw new NotImplementedException();
        }

        // udp
        protected virtual bool SendToAsync(SocketFlags socketFlags = SocketFlags.None) {
            throw new NotImplementedException();
        }

        protected void Error(int errorCode) {
            try {
                this.OnErrorEvent?.Invoke(errorCode);
            }
            catch (Exception exception) {
                Log.Error(exception);
            }
        }

        protected void OnClosed() {
            try {
                this.OnClosedEvent?.Invoke();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        public virtual void Close(bool closeSocket = true) {
            if (this._isClosed)
                return;

            this._isClosed = true;

            try {
                if (closeSocket) {
                    this.Socket?.Close();
                }
            }
            catch {
                // ignored
            }
            finally {
                this.Socket = null;
            }
        }

        public virtual void Dispose() {
            this.Close();

            if (this._isDisposed)
                return;

            this._isDisposed = true;
            this.OnErrorEvent = null;
            this.OnClosedEvent = null;
        }

        protected void CheckDisposedException() {
            if (this.IsDisposed)
                throw new Exception("Channel is disposed!");
        }
    }
}